package cn.quevo.ucenter.server.controller;

import java.security.NoSuchAlgorithmException;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.quevo.core.enums.UserStatusEnum;
import cn.quevo.core.lang.EnumUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import cn.quevo.core.enums.ResultStatusEnum;
import cn.quevo.core.exception.CustomException;
import cn.quevo.core.util.PasswordEncoder;
import cn.quevo.spring.SpringController;
import cn.quevo.ucenter.core.ResponseEntityBuild;
import cn.quevo.ucenter.core.entity.ClientInfo;
import cn.quevo.ucenter.core.entity.OauthRequest;
import cn.quevo.ucenter.core.entity.OauthResponse;
import cn.quevo.ucenter.core.entity.User;
import cn.quevo.ucenter.core.enums.GrantTypeEnum;
import cn.quevo.ucenter.core.enums.OauthResultStatusEnum;
import cn.quevo.ucenter.core.exception.OauthException;
import cn.quevo.ucenter.server.entity.AccessToken;
import cn.quevo.ucenter.server.entity.query.OauthClientQuery;
import cn.quevo.ucenter.server.entity.query.UserQuery;
import cn.quevo.ucenter.server.service.AccessTokenService;
import cn.quevo.ucenter.server.service.OauthClientService;
import cn.quevo.ucenter.server.service.OauthCodeService;
import cn.quevo.ucenter.server.service.UserService;

/**
 * 访问Token Controller
 *
 * @author July july_sky@foxmail.com
 * @version 1.0
 * @date 2018年3月23日 下午10:36:06
 * @Copyright ©2015-2035 湘豫(北京)科技有限公司. All Rights Reserved.
 */
@RestController("accessTokenController")
public class AccessTokenController extends SpringController {
    private final static Logger log = LoggerFactory.getLogger(AccessTokenController.class);
    @Resource(name = "oauthClientService")
    private OauthClientService oauthClientService;
    @Resource(name = "oauthCodeService")
    private OauthCodeService oauthCodeService;
    @Resource(name = "accessTokenService")
    private AccessTokenService accessTokenService;
    @Resource(name = "userService")
    private UserService userService;

    @RequestMapping("/accessToken")
    public HttpEntity<Object> accessToken(HttpServletRequest request) {
        try {
            // 构建OAuth请求
            OauthRequest oauthRequest = new OauthRequest(request);
            GrantTypeEnum grantType = this.formatGrantType(oauthRequest.getGrantType());
            if (grantType == null) {
                return ResponseEntityBuild.build(HttpStatus.OK,
                  new OauthResponse(OauthResultStatusEnum.INVALID_GRANT));
            }
            OauthClientQuery oauthClientQuery = new OauthClientQuery();
            oauthClientQuery.setClientId(oauthRequest.getClientId());
            oauthClientQuery.setClientSecret(oauthRequest.getClientSecret());
            ClientInfo clientInfo = this.oauthClientService.getOauthClient(oauthClientQuery);
            // 验证客户端是否存在
            if (clientInfo == null) {
                return ResponseEntityBuild.build(HttpStatus.OK,
                  new OauthResponse(OauthResultStatusEnum.INVALID_CLIENT));
            }
            // 检查客户端安全KEY是否正确
            if (!clientInfo.getClientSecret().equals(oauthClientQuery.getClientSecret())) {
                return ResponseEntityBuild.build(HttpStatus.OK,
                  new OauthResponse(OauthResultStatusEnum.UNAUTHORIZED_CLIENT));
            }
            switch (grantType) {
//        case AUTHORIZATION_CODE:
//            return this.authorizationCode(oauthRequest, clientInfo);
                case PASSWORD:
                    return this.password(oauthRequest, clientInfo);
                case REFRESH_TOKEN:
                    return this.refreshToken(oauthRequest, clientInfo);
                default:
                    throw new OauthException(HttpServletResponse.SC_OK, ResultStatusEnum.METHOD_NOT_EXISTS);
            }
        } catch (OauthException e) {
            return ResponseEntityBuild.build(HttpStatus.valueOf(e.getResponseStatus()), new OauthResponse(e));
        } catch (CustomException e) {
            return ResponseEntityBuild.build(HttpStatus.OK, new OauthResponse(e));
        } catch (Exception e) {
            return ResponseEntityBuild.build(HttpStatus.OK, new OauthResponse(ResultStatusEnum.UNKNOWN_EXCEPTION));
        }
    }

    /**
     * 刷新令牌
     *
     * @param oauthRequest
     * @param clientInfo
     * @return
     * @throws CustomException
     * @author July july_sky@foxmail.com
     * @date 2018年3月26日 下午5:21:26
     */
    private HttpEntity<Object> refreshToken(OauthRequest oauthRequest, ClientInfo clientInfo){
        AccessToken accessToken = this.buildAccessToken(oauthRequest);
        accessToken.setRefreshToken(oauthRequest.getRefreshToken());
        if (this.accessTokenService.updateRefreshToken(accessToken) <= 0) {
            return ResponseEntityBuild.build(HttpStatus.OK,
              new OauthResponse(OauthResultStatusEnum.INVALID_REFRESH_TOKEN));
        }
        OauthResponse response = new OauthResponse();
        response.setAccessToken(accessToken.getAccessToken()).setExpiresIn(accessToken.getAccessExpiredIn())
          .setRefreshToken(accessToken.getRefreshToken());
        return ResponseEntityBuild.ok(response);
    }

    /**
     * 密码模式
     *
     * @param oauthRequest
     * @param clientInfo
     * @return
     * @throws CustomException
     * @author July july_sky@foxmail.com
     * @date 2018年3月26日 下午4:46:55
     */
    private HttpEntity<Object> password(OauthRequest oauthRequest, ClientInfo clientInfo){
        UserQuery userQuery = new UserQuery();
        userQuery.setUserName(oauthRequest.getUserName());
        userQuery.setPassword(oauthRequest.getPassword());
        userQuery.setClientId(oauthRequest.getClientId());
        User user = this.userService.getUserByName(userQuery);
        try {
            if (user == null || !PasswordEncoder.passwdValidate(user.getPassword(), oauthRequest.getPassword())) {
                log.debug("密码错误:" + user.getPassword());
                return ResponseEntityBuild.build(HttpStatus.OK,
                  new OauthResponse(ResultStatusEnum.ACCOUNT_PASSWORD_ERROR));
            }
            UserStatusEnum userStatusEnum = EnumUtils.getEnum(user.getUserStatusCd(),UserStatusEnum.values());
            if (userStatusEnum == null) {
                userStatusEnum = UserStatusEnum.SUCCESS;
            }
            if (userStatusEnum != UserStatusEnum.SUCCESS) {
                return ResponseEntityBuild.build(HttpStatus.OK,
                  new OauthResponse(userStatusEnum));
            }
        } catch (NoSuchAlgorithmException e) {
            log.error(e.getMessage(), e);
            return ResponseEntityBuild.build(HttpStatus.OK,
              new OauthResponse(ResultStatusEnum.ACCOUNT_PASSWORD_ERROR));
        }
        AccessToken accessToken = buildAccessToken(oauthRequest);
        accessToken.setUserId(user.getUserId());
        accessToken.setRecordOwnerId(user.getRecordOwnerId());
        accessToken.setOperatorId(user.getSubjectId());
        return this.createAccessToken(accessToken);
    }

    /**
     * 验证码类型(暂时不用)
     *
     * @author July july_sky@foxmail.com
     * @date 2018年3月26日 上午11:00:32
     * @param oauthRequest
     * @return
     * @throws CustomException
     * @throws OAuthSystemException
     */
    /*private HttpEntity<Object> authorizationCode(OauthRequest oauthRequest, ClientInfo clientInfo)
           {
        OauthCodeQuery oauthCodeQuery = new OauthCodeQuery();
        oauthCodeQuery.setClientId(oauthRequest.getClientId());
        oauthCodeQuery.setAuthCode(oauthRequest.getAuthCode());
        OauthCode oauthCode = oauthCodeService.getOauthCode(oauthCodeQuery);
        if (oauthCode == null) {
            return ResponseEntityBuild.build(HttpStatus.OK,
                    new OauthResponse(OauthResultStatusEnum.INVALID_GRANT));
        }
        // 生成Access Token
        AccessToken accessToken = buildAccessToken(oauthRequest);
        accessToken.setUserId(oauthCode.getUserId());
        accessToken.setAuthCode(oauthCode.getAuthCode());
        return this.createAccessToken(accessToken);
    }*/

    /**
     * 获取授权类型枚举
     *
     * @param grantType
     * @return
     * @author July july_sky@foxmail.com
     * @date 2018年3月26日 上午10:53:45
     */
    private GrantTypeEnum formatGrantType(String grantType) {
        try {
            return GrantTypeEnum.valueOf(grantType.toUpperCase());
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 构造AccessToken
     *
     * @param oauthRequest
     * @return
     * @author July july_sky@foxmail.com
     * @date 2018年3月26日 下午4:38:20
     */
    private AccessToken buildAccessToken(OauthRequest oauthRequest) {
        AccessToken accessToken = new AccessToken();
        accessToken.setGrantType(oauthRequest.getGrantType());
        accessToken.setClientId(oauthRequest.getClientId());
        accessToken.setUserName(oauthRequest.getUserName());
        return accessToken;
    }

    /**
     * 创建授权码
     *
     * @param accessToken
     * @return
     * @author July july_sky@foxmail.com
     * @date 2018年3月26日 下午5:09:45
     */
    private HttpEntity<Object> createAccessToken(AccessToken accessToken){
        if (this.accessTokenService.createAccessToken(accessToken) <= 0) {
            return ResponseEntityBuild.build(HttpStatus.OK,
              new OauthResponse(OauthResultStatusEnum.INVALID_GRANT));
        }
        OauthResponse response = new OauthResponse();
        response.setAccessToken(accessToken.getAccessToken()).setRefreshToken(accessToken.getRefreshToken())
          .setIssuedAt(accessToken.getIssuedAt()).setExpiresIn(accessToken.getAccessExpiredIn())
          .setSubjectId(accessToken.getSubjectId());
        return ResponseEntityBuild.ok(response);
    }
}
